<!DOCTYPE html>
<html lang="zh-CN">

<!-- Head tag -->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  
  <meta name="baidu-site-verification" content="undefined" />
  <meta name="google-site-verification" content="undefined" />
  <meta name="msvalidate.01" content="undefined" />
  <meta name="360-site-verification" content="undefined" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- if page has tags, then add tags to keyword -->
  
  
  <meta name="keywords" content=",基础,tcp/http,">
  <!-- page.description has higher priority -->
  <meta name="description" content="简述TCP“3次握手，4次挥手”,结合抓包工具进行分析">
  <link rel="shortcut icon" href="/blog/img/logo.webp">
  <title>
    
    简述TCP“3次握手，4次挥手” | Evrn
    
  </title>

  <link rel="canonical" href="/blog/post/tcp-three-way-handshake.html">
  
<link rel="stylesheet" href="/blog/css/reset.css">

  
<link rel="stylesheet" href="/blog/css/layout.css">

  
<link rel="stylesheet" href="/blog/css/markdown.css">

  <!-- Pygments Highlight CSS -->
  
<link rel="stylesheet" href="/blog/css/highlight.css">

  
<link rel="stylesheet" href="/blog/css/geektutu.css">

  <!-- global function -->
  <script>
    window.globalAddScript = function (url, onload, onerror) {
      var s = document.createElement('script');
      s.src = url;
      onload && (s.onload = onload);
      onerror && (s.onerror = onerror);
      document.body.appendChild(s);
    }
    window.globalAddCss = function (url) {
      var s = document.createElement('link');
      s.rel = 'stylesheet';
      s.href = url;
      document.body.appendChild(s);
    }
    window.getPosition = function (ele) {
      var x = 0, y = 0;
      while (ele) {
        x += (ele.offsetLeft - ele.scrollLeft + ele.clientLeft);
        y += (ele.offsetTop - ele.scrollTop + ele.clientTop);
        ele = ele.offsetParent;
      }
      return { x: x, y: y };
    }
    window.getDom = function (str) { return document.querySelector(str) }
  </script>
  <!-- google ad -->
  
<meta name="generator" content="Hexo 4.2.0"></head>

<body>
    <header class="gkt-header col-xs-12 padding-0">
    <div id="gkt-nav" class="gkt-header-container">
        <a href="/blog/" class="gkt-header-title float-left">
            <img class="float-left" src="/blog/img/logo.webp" alt="">
            <span>Evrn</span>
        </a>
        <nav class="gkt-header-nav text-right">
            <ul>
                <li><a class="hidden-xs" href="/blog/">主页</a></li>
                <li><a href="/blog/series/">专题</a></li>
                <li><a href="/blog/archives/">归档</a></li>
                <li><a href="/blog/post/link.html">友链</a></li>
                <li><a href="/blog/post/about.html">留言</a></li>
            </ul>
        </nav>
    </div>
    <div id="gkt-cate-nav" class="gkt-header-container hidden-xs">
        
        <nav class="gkt-header-nav float-left">
            <ul>
                
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-learn-interface.html">Golang学习</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-asm-indroduce.html">Golang汇编</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left active">
                    <a class="float-left" href="/blog/post/tcp-three-way-handshake.html">基础</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-how-to-build.html">Golang底层</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/mysql-index-use.html">MySQL</a>
                    
                </li>
                
            </ul>
        </nav>
    </div>
</header>
<div style="height: 44px"></div>
<div class="hidden-xs" style="height: 44px"></div>
<script>
    (function () {
        window.addEventListener('scroll', function () {
            if (window.innerWidth < 768) {
                return;
            }
            var nav = document.querySelector('#gkt-nav');
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            scrollTop > 50 && (nav.classList.add('hide'));
            scrollTop <= 50 && (nav.classList.remove('hide'));
        });
        var cateNavs = document.querySelectorAll('#gkt-cate-nav>nav>ul>li');
        [].slice.call(cateNavs).forEach(function (item) {
            var sub = item.querySelector('.gkt-sub-cate');
            if (!sub) return;
            item.addEventListener('mouseenter', function (e) { sub.style.display = 'block'; }, false);
            item.addEventListener('mouseleave', function (e) { sub.style.display = 'none'; }, false);
        })
    })();
</script>
    <!-- Main Content -->
    <div class="main-container">
        <!-- Main Content -->
<main class="col-xs-12 padding-0 markdown-it">
    <!-- Post Container -->
    
    
    <!-- Post Content -->
<div class="float-left post-container box-shadow">
    <div class="u-arrow-wrapper hidden-xs">
        
        <a class="float-left" href="/blog/post/go-asm-datastruct.html"><i class="u-arrow-left"></i></a>
        
        
        <a class="float-right" href="/blog/post/tcp-what-is-https.html"><i class="u-arrow-right"></i></a>
        
    </div>
    <article class="col-xs-12">
        <h1> 简述TCP“3次握手，4次挥手” </h1>

        

        
        <div class="hidden-lg hidden-md series_links">
            <p> <strong> 基础系列文章链接：</strong></p>
            <ul>
                
                <li>
                    <a href="/blog/post/tcp-three-way-handshake.html">简述TCP“3次握手，4次挥手”</a>
                    <span class="post-item-date">(Feb 21, 2020)</span>
                </li>
                
                <li>
                    <a href="/blog/post/tcp-what-is-https.html">简述HTTPS/SSL/TLS运行过程</a>
                    <span class="post-item-date">(Feb 22, 2020)</span>
                </li>
                
            </ul>
        </div>
        
        
        
        <h2 id="什么是“3次握手，4次挥手”"><a href="#什么是“3次握手，4次挥手”" class="headerlink" title="什么是“3次握手，4次挥手”"></a>什么是“3次握手，4次挥手”</h2><p>TCP是一种面向连接的单播协议，在发送数据前，通信双方必须在彼此间建立一条连接。所谓的“连接”，其实是客户端和服务器的内存里保存的一份关于对方的信息，如ip地址、端口号等。</p>
<p>TCP可以看成是一种字节流，它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中，双方需要交换一些连接的参数。这些参数可以放在TCP头部。</p>
<p>TCP提供了一种可靠、面向连接、字节流、传输层的服务，采用三次握手建立一个连接。采用4次挥手来关闭一个连接。</p>
<h2 id="TCP简介"><a href="#TCP简介" class="headerlink" title="TCP简介"></a>TCP简介</h2><p>TCP是一种面向连接的<strong>单播</strong>协议，在发送数据前，通信双方必须在彼此间建立一条连接。所谓的“连接”，其实是客户端和服务器的内存里保存的一份关于对方的信息，如ip地址、端口号等。</p>
<blockquote>
<p><strong>单播特点</strong>：一个单个的发送者和一个接受者之间通过网络进行的通信。</p>
</blockquote>
<blockquote>
<p>1、服务器及时响应客户机的请求</p>
</blockquote>
<blockquote>
<p>2、服务器针对每个客户不同的请求发送不同的数据，容易实现个性化服务。</p>
</blockquote>
<blockquote>
<p><strong>多播特点</strong>：一个发送者和多个接受者之间的通信。</p>
</blockquote>
<blockquote>
<p><strong>广播特点</strong>：主机之间“一对所有”的通讯模式，网络对其中每一台主机发出的信号都进行无条件复制并转发，所有主机都可以接收到所有信息（不管你是否需要）。</p>
</blockquote>
<blockquote>
<p>1、网络设备简单，维护简单，布网成本低廉。</p>
</blockquote>
<blockquote>
<p>2、由于服务器不用向每个客户机单独发送数据，所以服务器流量负载极低。</p>
</blockquote>
<p>TCP可以看成是一种字节流，它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中，双方需要交换一些连接的参数。这些参数可以放在TCP头部。</p>
<p>TCP提供了一种可靠、面向连接、字节流、传输层的服务，采用<strong>三次握手建立一个连接。采用4次挥手来关闭一个连接</strong>。</p>
<h3 id="TCP协议包组成"><a href="#TCP协议包组成" class="headerlink" title="TCP协议包组成"></a>TCP协议包组成</h3><p><img src="http://server-1256789886.cos.ap-guangzhou.myqcloud.com/blog_go/evrn/evrn/evrn/64778b5ca20044958786d2b4f1bed439.png" alt="imagepng"></p>
<p>源端口和目的端口在TCP层确定双方进程，</p>
<p><strong>序列号</strong>（32位，<strong>Sequence number</strong>）如果将字节流看作在两个应用程序间的单向流动，则TCP用序号对每个字节进行计数，这个序列号表示的就是报文段数据中的<strong>第一个字节的序号</strong></p>
<p><strong>确认号</strong>（32位，<strong>Acknoledgment number</strong>），表示发送方<strong>期待接收的下一个序列号</strong>，即<strong>最后被成功接收的数据字节序列号</strong>加1，这个字段只有在ACK位被启用的时候才有效。</p>
<h4 id="标志位"><a href="#标志位" class="headerlink" title="标志位"></a><strong>标志位</strong></h4><p><strong>URG</strong> 紧急:表示紧急指针（<strong>Urgentpointer</strong>）有效。当 URG=1 时，它告诉系统此报文中有紧急数据，应优先传送(比如紧急关闭)，这要与紧急指针字段配合使用。<br><strong>ACK</strong>  确认:仅当 ACK=1 时<strong>确认号</strong>字段才有效。建立 TCP 连接后，所有报文段都必须把 ACK 字段置为 1。<br><strong>PSH</strong>  推送:若 TCP 连接的一端希望另一端立即响应，PSH 字段便可以“催促”对方，不再等到缓存区填满才发送。<br><strong>RST</strong>  复位:若 TCP 连接出现严重差错，RST 置为 1，断开 TCP 连接，再重新建立连接。<br><strong>SYN</strong> 同步:用于建立和释放连接，稍后会详细介绍。<br><strong>FIN</strong>  终止:用于释放连接，<strong>发送方</strong>发出 FIN=1表明<strong>我</strong>已经发送完毕，要求释放 TCP 连接。</p>
<p><strong>数据偏移</strong>(4位，Data offset)</p>
<p>以4bytes为单位，<strong>最大为15</strong>，因此头部最大长度为15<em>4 bytes=60字节，*</em>最小为5**，因此头部最小为20字节（可变选项为空）</p>
<p>当一个连接被建立或被终止时，交换的报文段只包含TCP头部，而没有数据。</p>
<h2 id="三次握手"><a href="#三次握手" class="headerlink" title="三次握手"></a>三次握手</h2><h3 id="意义"><a href="#意义" class="headerlink" title="意义"></a>意义</h3><p>三次握手的作用简单来说就是<strong>明确自己和对方的收，发能力是正常的</strong></p>
<p>第一次：客户端发送请求连接网络包，这是第一次握手。</p>
<p>第二次：服务端收到请求连接的网络包，表明这个客户端发送能力，自己的接受能力都没问题，随即回复确认网络包，这是第二次握手。</p>
<p>第三次：客户端收到服务端发来的确认网络包，它知道表明自己的收，发能力都没问题，而且服务端的收，发能力也没问题，到此客户端确认完毕。但是此时服务端不知道自己有没成功发送，所以客户端针对这次接收发送确认包，这是第三次握手，此时如果服务端收到这个确认网络包，说明自己的发送能力也没问题。</p>
<table>
<thead>
<tr>
<th></th>
<th>服务端确认</th>
<th>客户端确认</th>
</tr>
</thead>
<tbody><tr>
<td>第一次握手</td>
<td>对方能发，自己能收</td>
<td>-</td>
</tr>
<tr>
<td>第二次握手</td>
<td>-</td>
<td><strong>对方能收，发；自己能收，发</strong></td>
</tr>
<tr>
<td>第三次握手</td>
<td><strong>对方能收，发；自己能收，发</strong></td>
<td>-</td>
</tr>
</tbody></table>
<p>至此，双方各自都确认对方收，发能力没问题，可以<strong>建立连接</strong>。</p>
<h3 id="状态转换"><a href="#状态转换" class="headerlink" title="状态转换"></a>状态转换</h3><p>知道三次握手原理后，开始深入探索。</p>
<p>以下就是整个三次握手状态转变过程。</p>
<p><img src="http://river.marvelthanos.com/blog/20200305/nIHvfEJ5yQWz.webp" alt="mark"></p>
<p>简单分析一下整个过程</p>
<p>1.当建立一个新的连接时，会发送一个数据包，SYN标志设为1。<strong>序列号</strong>字段为包含由这个主机通过<strong>特定算法算出的</strong>的初始序号ISN（InitialSequenceNumber）图中的x。</p>
<p>2.接收端收到这个数据包后，首先<strong>也</strong>通过<strong>特定算法算出的</strong>另一个初始序号ISN（图中的y），也设置<strong>SYN标志位为1</strong>(ACKbit=1)，同时确认号为发送确认的一端所期望收到的下一个字节的序号，也即成功接收的数据序号+1。因为<strong>SYN标志会消耗一个序号</strong>，所以<strong>确认号</strong>为接收到的这个ISN（x）加SYN消耗掉的序列号数（1）也就是ACKnum=x+1。</p>
<p>3.主动连接方收到接收端回复的数据包后也作出回复。首先设置<strong>ACK标志</strong>位为1，<strong>序列号</strong>字段为刚刚接收到的ACKnum的值（图中的x+1），因为SYN标志消耗一个序号，<strong>确认号</strong>字段为收到的序列号字段+1，也就是ACKnum=y+1，至此，TCP连接建立完成。</p>
<p>三次握手用表格演示</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>方向</th>
<th>seqnum</th>
<th>acknum</th>
<th>SYN</th>
<th>ACK</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>A-&gt;B</td>
<td>10000(ISN)</td>
<td>0</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>2</td>
<td>A&lt;-B</td>
<td>20000(ISN)</td>
<td>10000+1=10001</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>A-&gt;B</td>
<td>10001</td>
<td>20000+1=20001</td>
<td>0</td>
<td>1</td>
</tr>
</tbody></table>
<h3 id="关于序列号回绕"><a href="#关于序列号回绕" class="headerlink" title="关于序列号回绕"></a>关于序列号回绕</h3><p>因为ISN是<strong>随机</strong>的，所以序列号容易就会超过2^31-1. 而tcp对于丢包和乱序等问题的判断都是依赖于序列号大小比较的。此时就出现了所谓的tcp序列号回绕（sequence wraparound）问题。怎么解决？</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* The next routines deal with comparing 32 bit unsigned ints</span></span><br><span class="line"><span class="comment">* and worry about wraparound (automatic with unsigned arithmetic).</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">before</span><span class="params">(__u32 seq1, __u32 seq2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (__s32)(seq1-seq2) &lt; <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> after(seq2, seq1) before(seq1, seq2)</span></span><br></pre></td></tr></table></figure>

<p>上述代码是内核中的解决回绕问题代码。</p>
<p>为什么（_<em>s32）(seq1-seq2)&lt;0就可以判断<strong>seq1&lt;seq2</strong>呢？</em></p>
<p>_这里的<em>\</em>s32是有符号整型，而__u32则是无符号整型。__s32是有符号整型的意思，而__u32则是无符号整型。</p>
<p>当没发生回绕时seq1&lt;seq2(否则就是丢包或者乱序)，所以seq1-seq2&lt;0。 </p>
<p>当发生回绕的时候</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">假设seq1&#x3D;255， seq2&#x3D;1（发生了回绕）。</span><br><span class="line">seq1 &#x3D; 1111 1111 seq2 &#x3D; 0000 0001</span><br><span class="line">我们希望比较结果是</span><br><span class="line"> seq1 - seq2&#x3D;</span><br><span class="line"> 1111 1111</span><br><span class="line">-0000 0001</span><br><span class="line">-----------</span><br><span class="line"> 1111 1110</span><br><span class="line"></span><br><span class="line">由于我们将结果转化成了有符号数，由于最高位是1，因此结果是一个负数。</span><br><span class="line">因此seq1 - seq2 &lt; 0 因此 seq1 &lt; seq2</span><br></pre></td></tr></table></figure>

<p>经过计算发现 seq1 &lt; seq2 所以不管有没有发生回绕，只要 before算出来 seq1  &lt; seq2 这个包就是正常的，可以进行下一步处理，管你回不回绕。</p>
<blockquote>
<p>关于回绕网上抄的都是同一个版本，通常后面会加一句</p>
<p>1111 1110，负数的绝对值为<br>0000 0001 + 1 = 0000 0010 = 2</p>
<p>对于对底层比较熟悉的人可能一看就懂，但是像我这种半桶水看到这里就蒙了，翻查资料才发现，其实我们<strong>只需要知道是负数</strong>就好了，至于绝对值是多少其实跟tcp回绕没有半毛钱关系。</p>
<p>但是1111 1110为什么是-2呢？</p>
<p>首先，计算机为了计算方便所有<strong>有符号数都是用补码形式表示</strong></p>
<p><strong>那么补码怎么计算出来的呢？</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">1、正数：原码和补码一致</span><br><span class="line">2、负数：原码取反（符号位不变），然后加1</span><br></pre></td></tr></table></figure>

<p>所以对于负数，我们应该先减一，再取反，那就是他的原码了</p>
<p>对于1111 1110 - 1 =1111 1101 再取反 = 1000 0010 = -2 </p>
<p>因此绝对值是 2  然而算出这个在这里其实没有什么卵意义 。</p>
<p>还有个知识点是</p>
<p>1、计算机实现中减法其实也是用加法机来实现，1-2=&gt; 1+(-2)</p>
<p>2、对于cpu没有有符号和无符号数之分，反正就是一把梭，就是加</p>
</blockquote>
<h2 id="数据传输"><a href="#数据传输" class="headerlink" title="数据传输"></a>数据传输</h2><p>数据传输的过程主要由序列号，确认号和<strong>有效数据</strong>长度，三个字段来做包顺序的控制</p>
<p>序列号：之前收到数据包的确认号字段字段</p>
<p>确认号：之前收到的数据包的序列号字段加上TCP<strong>有效数据</strong>长度</p>
<p>而之前说过一旦tcp建立连接后，ACK标志位就一直为1，这里就不再累述。</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>方向</th>
<th>seqnum</th>
<th>acknum</th>
<th>数据长度</th>
<th>数据包长度</th>
</tr>
</thead>
<tbody><tr>
<td>4</td>
<td>A-&gt;B</td>
<td>10001</td>
<td>20001</td>
<td>1460</td>
<td>1514</td>
</tr>
<tr>
<td>5</td>
<td>A&lt;-B</td>
<td>20001</td>
<td>10001+1460=11461</td>
<td>0</td>
<td>54</td>
</tr>
<tr>
<td>6</td>
<td>A-&gt;B</td>
<td>11461</td>
<td>20001+0=20001</td>
<td>1460</td>
<td>1514</td>
</tr>
<tr>
<td>7</td>
<td>A&lt;-B</td>
<td>20001</td>
<td>11461+1460=12921</td>
<td>0</td>
<td>54</td>
</tr>
</tbody></table>
<p>数据长度指的就是有效的数据长度，这是tcp数据包最大的有效数据长度，而数据包长度就是整个数据包的长度，那最大有效数据包长度为什么是1514？    首先介绍几个概念</p>
<h3 id="数据包进入协议栈分层"><a href="#数据包进入协议栈分层" class="headerlink" title="数据包进入协议栈分层"></a>数据包进入协议栈分层</h3><h4 id="MSS：Maximum-Segment-Size：最大报文段长度的缩写"><a href="#MSS：Maximum-Segment-Size：最大报文段长度的缩写" class="headerlink" title="MSS：Maximum Segment Size：最大报文段长度的缩写"></a>MSS：Maximum Segment Size：最大报文段长度的缩写</h4><p>是<strong>TCP协议</strong>里面的一个概念, 在建立连接的时候，三次握手<strong>前两次</strong>的SYN包中的自选字段来表明各自的MSS值。这个值通常<strong>用MTU值</strong>（通常1500）减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes，所以一般MSS值=1500-20-20=<strong>1460</strong></p>
<h4 id="MTU：Maximum-Transmission-Unit-：最大传输单元"><a href="#MTU：Maximum-Transmission-Unit-：最大传输单元" class="headerlink" title="MTU：Maximum Transmission Unit ：最大传输单元"></a>MTU：Maximum Transmission Unit ：最大传输单元</h4><p><strong>链路层</strong>的一个限制，不同链路层有不同的MTU，在Ethernet中，MTU为<strong>1500字节</strong>。</p>
<p>如果IP层有一个数据报要传，而且数据帧的长度比链路层的MTU还大，那么IP层就需要进行分片( fragmentation)，即把数据报分成干片，这样每一片就都小于MTU。</p>
<p><img src="http://river.marvelthanos.com/blog/20200306/nKjORfANTzI2.png?imageslim" alt="mark"></p>
<h4 id="数据链路层封装"><a href="#数据链路层封装" class="headerlink" title="数据链路层封装"></a><strong>数据链路层</strong>封装</h4><p><img src="http://river.marvelthanos.com/blog/20200306/ooJQz2QjYb5r.webp" alt="mark"></p>
<p>在<strong>数据链路层</strong>，将从上层传下来的1500字节的数据，加上目的地址，源地址，类型，crc等字段后再次打包，再加上<strong>7字节</strong>的前导同步码和<strong>1字节</strong>的帧开始定界符组成一个完整的数据包后交给物理层进行传输。</p>
<p>整个过程就是</p>
<p><img src="http://river.marvelthanos.com/blog/20200306/KSuWqh8NtMyt.webp" alt="mark"></p>
<p>回归到一开始的问题，1514这个数字怎么来的。</p>
<p>首先按照之前的算法，一个完整数据包最大的长度应该是7+1+14+1500+4=1526字节</p>
<p>在实际的网络环境中当数据帧到达网卡时，</p>
<p>在物理层上网卡要<strong>先去掉前导同步码和帧开始定界符</strong>，然后对帧进行CRC检验，</p>
<p>如果帧校验和错，就丢弃此帧。</p>
<p>如果校验和正确，就判断帧的目的硬件地址是否符合自己的接收条件（目的地址是自己的物理硬件地址、广播地址)、可接收的多播硬件地址等），如果符合，就将帧交“设备驱动程序”做进一步处理。</p>
<p>这时我们的抓包软件才能抓到数据，因此，抓包软件抓到的是去掉前导同步码、帧开始分界符、CRC校验之外的数据，也就是1526-7-1-4=<strong>1514 字节</strong></p>
<p>而这1514字节由14 bytes 以太网首部，20 bytes IP首部，20 bytes TCP首部 和 1460 bytes 应用数据组成。如果没有应用数据，至于TCP首部的数据包（比如ACK确认包）它的大小就是54字节。</p>
<h2 id="四次挥手"><a href="#四次挥手" class="headerlink" title="四次挥手"></a>四次挥手</h2><p><img src="http://river.marvelthanos.com/blog/20200306/dUCMPDj8TONv.png?imageslim" alt="mark"></p>
<p>1：(A) –&gt; [FIN/ACK] –&gt; (B)<br>客户端A没有要发送给服务端B的数据了，想要关闭链接，则发送一个FIN=1，ACK=1的包，注意，<strong>FIN也是会消耗一个序号</strong>，告诉B可以关闭连接了，我没有什么数据要给你了。<br>2：(A) &lt;– [ACK] &lt;– (B)<br>然后B会发送ACK=1的包给A，告诉A我知道你没有什么想给我的了，但是我还有数据要给你，你<strong>先等下</strong>，我<strong>先不</strong>想FINISH呢。<br>3：(A) &lt;– [FIN/ACK] &lt;– (B)<br>等B把数据都发送给A之后，B会再次发送一个包，这次FIN=1，表示我这边也想关闭了，咱俩一起关把。<strong>在2和3之间，可能还会有很多B-&gt;A的传递，ack均为80001。</strong><br>4：(A) –&gt; [ACK] –&gt; (B)<br>然后A回应一个ACK，表示我知道了，一起关吧。B收到这个ACK后，就会CLOSE。但是实际上A不会直接CLOSE，还会进入一个<strong>等待时间</strong>状态<strong>TIME_WAIT</strong>，持续<strong>2倍的MSL</strong>（Maximum Segment Lifetime，报文段在网络上能存活的最大时间）。过了这个状态，才会CLOSE。</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>方向</th>
<th>seqnum</th>
<th>acknum</th>
<th>FIN</th>
<th>ACK</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>A-&gt;B</td>
<td>12921</td>
<td>20001</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>A&lt;-B</td>
<td>20001</td>
<td>12921+1=12922</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>A&lt;-B</td>
<td>20001</td>
<td>12922</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>4</td>
<td>A-&gt;B</td>
<td>12922</td>
<td>20001+1=2000</td>
<td>0</td>
<td>1</td>
</tr>
</tbody></table>
<h3 id="特殊状态"><a href="#特殊状态" class="headerlink" title="特殊状态"></a>特殊状态</h3><p>在四次挥手中有两个状态是特别值得留意的，分别是</p>
<h4 id="TIME-WAIT"><a href="#TIME-WAIT" class="headerlink" title="TIME_WAIT"></a>TIME_WAIT</h4><p>主动关闭方(不论是客户端还是服务端)发送的最后一个 ack(fin) ，有可能丢失，这时被动方会重新发fin, </p>
<p>当这个fin包到达目的地的时候，有两种异常情况</p>
<p>1.如果这时主动方处于 CLOSED 状态 ，就会响应 rst 而不是 ack，这样就不能优雅的关闭连接。</p>
<p>2.如果此时同一个四元组（源IP地址、目的IP地址、源端口、目的端口）已经重新建立新的连接并开始通讯，这时突然收到fin数据包，就会被误认为是关闭请求，从而扰乱新的通讯因此主动方要处于 TIME_WAIT 状态一段时间，而不能是 CLOSED 。</p>
<h5 id="为什么TIME-WAIT要持续两倍MSL"><a href="#为什么TIME-WAIT要持续两倍MSL" class="headerlink" title="为什么TIME_WAIT要持续两倍MSL?"></a>为什么TIME_WAIT要持续两倍MSL?</h5><p>MSL(Maximum Segment Lifetime)报文最大生存时间 ，它指的是任何报文在网络上存在的最长时间，超过这个时间的报文将会被丢弃。</p>
<p>为什么是两倍MSL，首先，如果主动关闭方发出的最后一个ask报文可能会丢失，而被动关闭方一直收不到这个ask就会重新发送fin。</p>
<p>这时候就有极端情况如果最后一个ask刚好经历了MSL个时间才到达目的地，而到达前的一瞬间，被动关闭方刚好重发了一个fin，此时这个fin又刚好要经历MSL才到达目的地，此时TIWE_WAIT状态一直要维持住的话，最少就要MSL+MSL=2MSL的时间，这段时间内如果收不到新的fin，主动关闭方才可以认为此时已经可以安全关闭连接了。</p>
<h5 id="产生问题及解决"><a href="#产生问题及解决" class="headerlink" title="产生问题及解决"></a>产生问题及解决</h5><p>大量的短连接关闭的时候就会导致大量的time_wait,解决办法就是 配置内核缩短tcp_fin_timeout 时间(默认30秒) 或者启用socket快速回收机制。</p>
<h4 id="CLOSE-WAIT"><a href="#CLOSE-WAIT" class="headerlink" title="CLOSE_WAIT"></a>CLOSE_WAIT</h4><p>被动关闭方收到主动关闭方发送的fin但是又没有及时的发送下一个fin,就会一直处于close_wait状态,fin信号通常由上层应用主动发出，出现这种情况这种情况一般就是代码出问题了,检查代码。</p>
<h2 id="wireshark-抓包注意事项"><a href="#wireshark-抓包注意事项" class="headerlink" title="wireshark 抓包注意事项"></a>wireshark 抓包注意事项</h2><p><a href="https://blog.csdn.net/cumirror/article/details/7054496" target="_blank" rel="noopener">wireshark 过滤语法详解</a></p>
<p>在我们用wireshark抓包的时候有几个细节要注意的</p>
<p>1.</p>
<p>为了简化显示，概况中显示的序列号其实只是相对序列号，真正的序列号要在详情里面看<img src="http://river.marvelthanos.com/blog/20200306/z1AphxSQjqLt.png?imageslim" alt="mark"></p>
<p>2.不同的行对应不同的协议</p>
<img src="http://river.marvelthanos.com/blog/20200306/Cs6HMgSM4aF5.png?imageslim" alt="mark" style="zoom:150%;" />



<h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://www.cnblogs.com/qcrao-2018/p/10182185.html" target="_blank" rel="noopener">三次握手,四次挥手</a></p>
<p><a href="https://blog.csdn.net/huaishu/article/details/93739446" target="_blank" rel="noopener">TCP包的seq和ack号计算方法</a></p>
<p><a href="https://www.jianshu.com/p/f9a5b07d99a2" target="_blank" rel="noopener">TCP分段与IP分片</a></p>
<p><a href="https://www.cnblogs.com/qcrao-2018/p/10285348.html" target="_blank" rel="noopener">深度解密HTTP通信细节</a></p>
<p><a href="https://www.zhihu.com/question/24598268" target="_blank" rel="noopener">解析 http 协议是否要处理粘包？</a></p>
<p><a href="https://blog.csdn.net/u011414200/article/details/47948401" target="_blank" rel="noopener">wireshark数据对应分析</a></p>
<p><a href="https://mp.weixin.qq.com/s?__biz=MzU1OTg5NDkzOA==&mid=2247483846&idx=1&sn=916bb620cfd9d58a663df12872b20ab0&chksm=fc111933cb669025b733cb7afb29c59753a2ae13707f6178a57801ee0e213d3fa8309dc2b91e&scene=126&sessionid=1583286680&key=15dfc010032bcd642a4c154d799f04d3cce32ce7a115aeb0d19d9d4b3d362ea261c829d07b3ed6e7393a068498944d237b2b03fcb0c9dfa192816e107d41cdcedb119dffbdaebec8232961b23c1b4a30&ascene=1&uin=MjMxMDg2MDc0MQ%3D%3D&devicetype=Windows+10&version=62080079&lang=zh_CN&exportkey=A4cEOCVJkLwInVWLeudjAy4%3D&pass_ticket=JH11Z3gK%2ByC45n0vMuT%2FbM7zHHOAOZep0rpK0qqjvG0SW3uvg%2B10f9rQYv6b%2F5zo" target="_blank" rel="noopener">为什么tcp的TIME_WAIT状态要维持2MSL</a></p>
<p><a href="https://blog.csdn.net/huaishu/article/details/72124045" target="_blank" rel="noopener">TCP-IP详解卷-基础知识 IP TCP UPD 协议</a></p>
<p><a href="https://blog.csdn.net/qq_32815525/article/details/79177255" target="_blank" rel="noopener">理解TCP序列号（Sequence Number）和确认号（Acknowledgment Number）</a></p>

        
<script src="/blog/js/qrious.min.js"></script>


        <hr>
        <div>
            
            <p>
                <span>专题: </span>
                
                <a href="/blog/series/#基础">
                    <code key="基础" class="post-label">基础</code>
                </a>
                
            </p>
            
            <p>
                <span>本文发表于 2020-02-21，最后修改于 2020-03-10。</span>
            </p>
            <!-- 文章末尾的提示 start -->
            
            
            
                
            <!-- 文章末尾的提示 end -->
        </div>
        <hr />
        <p name="pagination" style="font-size: 1.2em">
    
    <a class="float-left" href="/blog/post/go-asm-datastruct.html">上一篇 « Golang汇编(四)-汇编眼中的数据结构</a>
    
    
    <a class="float-right" href="/blog/post/tcp-what-is-https.html">下一篇 » 简述HTTPS/SSL/TLS运行过程</a>
    
</p>
    </article>
    <!-- 赞赏 -->
    <!--打赏-->

    <!-- 推荐阅读三篇文章 -->
    <div class="col-xs-12">
        <h3>推荐阅读</h3>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/tcp/https.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#基础"
                    title="基础">基础</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#tcp/http"
                    title="tcp/http">tcp/http</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/tcp-what-is-https.html" class="title">
                简述HTTPS/SSL/TLS运行过程
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-02-22，</span>
                    <span class="hidden-xs">全文5324字，</span>
                    <span>阅读约18分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/tcp/tcp.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#基础"
                    title="基础">基础</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#tcp/http"
                    title="tcp/http">tcp/http</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/tcp-three-way-handshake.html" class="title">
                简述TCP“3次握手，4次挥手”
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-02-21，</span>
                    <span class="hidden-xs">全文9978字，</span>
                    <span>阅读约34分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang基础"
                    title="Golang基础">Golang基础</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-method-params.html" class="title">
                从切片修改引发的go参数传递思考
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2019-04-19，</span>
                    <span class="hidden-xs">全文5452字，</span>
                    <span>阅读约19分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
    </div>
    <div class="col-xs-12">
        <!-- 标签列表 -->
        <!-- Featured Tags -->
<style>
    #featured-tag .post-tag-item {
        font-size: 12px;
        line-height: 30px;
        display: inline-block;
        height: 30px;
        margin: 5px 0px;
        padding: 0 7px;
        color: #333;
        border-radius: 15px;
        background: #f6f6f6;
    }

    #featured-tag .post-tag-item:hover {
        color: #337ab7;
    }
</style>
<div id="featured-tag">
    
    <a class="post-tag-item" href="/blog/tags/#关于我" title="关于我"
        rel="1">#关于我 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#友链" title="友链"
        rel="1">#友链 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang汇编" title="Golang汇编"
        rel="4">#Golang汇编 (4) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang底层" title="Golang底层"
        rel="5">#Golang底层 (5) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang" title="Golang"
        rel="8">#Golang (8) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang基础" title="Golang基础"
        rel="1">#Golang基础 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang源码" title="Golang源码"
        rel="2">#Golang源码 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#基础" title="基础"
        rel="2">#基础 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#tcp/http" title="tcp/http"
        rel="2">#tcp/http (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL" title="MySQL"
        rel="3">#MySQL (3) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL优化" title="MySQL优化"
        rel="1">#MySQL优化 (1) </a>
    
</div>
    </div>
    <!-- 评论 -->
    <div class="col-xs-12">
        
    </div>
</div>
<aside class="float-left gkt-sidebar hidden-xs hidden-sm">
    <div style="clear: both"></div>
    <div class="gkt-sidebar-wrapper">
        <section class="box-shadow"><style>
    .gkt-summary {
        border: 1px solid #DDDDDD;
        border-radius: 3px;
        padding: 5px;
        width: 100%;
    }


    .gkt-summary nav {
        overflow: hidden;
    }

    .gkt-summary nav a {
        display: inline-block;
        text-align: center;
        color: #333;
        font-size: 12px;
    }

    .gkt-summary nav span {
        display: block;
    }

    .gkt-summary nav .middle {
        border-left: 1px solid #eaecef;
        border-right: 1px solid #eaecef;
    }

    .gkt-summary .number {
        font-weight: bold;
    }

    .gkt-summary .link-list {
        margin-top: 5px;
        margin-bottom: -5px;
        padding-top: 7px;
        border-top: 1px dashed #999;
        display: flex;
    }

    .gkt-summary .link-list a {
        flex: 1;
    }

    .gkt-summary .link-list img {
        width: 25px;
        height: 25px;
    }
</style>

<div class="gkt-summary">
    <nav>
        <a href="/blog/" class="col-xs-4">
            <number class="number">15</number><span>文章</span>
        </a>
        <a href="/blog/series" class="col-xs-4 middle">
            <number class="number">5</number><span>专题</span>
        </a>
        <a href="/blog/tags" class="col-xs-4">
            <number class="number">11</number><span>标签</span>
        </a>
    </nav>

    
    <div class="link-list">
        
        <a href="https://marvelthanos.com" target="_blank" rel="noopener"><img src="/blog/img/related_links/thanos.webp" /></a>
        
    </div>
    
</div></section>
        
        
        <section class="gkt-sidebar-content box-shadow">
            <strong>基础</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/tcp-three-way-handshake.html"
                        class="gkt-sidebar-active">简述TCP“3次握手，4次挥手”</a>
                    
                    <!-- Table of Contents -->
<div id="sidebar-toc">
  <!-- TOC  -->
  
  <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#什么是“3次握手，4次挥手”"><span class="toc-nav-text">什么是“3次握手，4次挥手”</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#TCP简介"><span class="toc-nav-text">TCP简介</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#三次握手"><span class="toc-nav-text">三次握手</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#数据传输"><span class="toc-nav-text">数据传输</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#四次挥手"><span class="toc-nav-text">四次挥手</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#wireshark-抓包注意事项"><span class="toc-nav-text">wireshark 抓包注意事项</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#参考文章"><span class="toc-nav-text">参考文章</span></a></li></ol>
  
</div>

<script>
  (function () {
    var h2 = document.querySelectorAll('article h2');
    var h3 = document.querySelectorAll('article h3');
    var linkList = document.querySelectorAll('#sidebar-toc a');

    function findLinkElement(name) {
      for (var i = 0; i < linkList.length; i++) {
        var items = linkList[i].href.split('#');
        if (items && items[items.length - 1] === encodeURIComponent(name)) {
          return i;
        }
      }
      return -1;
    }

    function activeLink(titleList) {
      var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
      for (var i = titleList.length - 1; i >= 0; i--) {
        if (scrollTop - titleList[i].offsetTop > 0) {
          var index = findLinkElement(titleList[i].id);
          index != -1 && linkList[index].classList.add('gkt-sidebar-active');
          break;
        }
      }
    }

    window.addEventListener("scroll", function (e) {
      [].slice.call(linkList).forEach(function (link) {
        link.classList.remove('gkt-sidebar-active');
      })
      activeLink(h2);
    })
  })();
</script>
                    
                </li>
                
                <li>
                    <a href="/blog/post/tcp-what-is-https.html"
                        class="">简述HTTPS/SSL/TLS运行过程</a>
                    
                </li>
                
            </ul>
        </section>
        
        
        <section class="box-shadow">
            <strong>最近的文章</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-learn-lib-sorts.html">Go源码学习 sort包</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html">EXPLAIN-执行计划查询命令</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html">《高性能MySQL》笔记-索引基础</a>
                </li>
                
            </ul>
        </section>
        <section class="box-shadow text-center">
            
        </section>
    </div>
</aside>

<script>
    (function () {
        var ele = getDom('.gkt-sidebar-content')
        var wrapper = getDom('.gkt-sidebar-wrapper')
        var last = 0
        window.addEventListener("scroll", function (e) {
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            var isDown = scrollTop > last;
            var pos = getPosition(ele).y - scrollTop;
            var downLimit = 50;
            var upLimit = -100;
            // uarrow.style.marginTop = scrollTop + 'px';
            isDown && pos <= downLimit && wrapper.classList.add("gkt-sidebar-fixed");
            !isDown && pos > upLimit && wrapper.classList.remove("gkt-sidebar-fixed");
            last = scrollTop
        })
    })();
    (function () {
        function resizeUArrow() {
            var s = getDom('.u-arrow-wrapper').style
            var pc = getDom('.post-container')
            s.left = getPosition(pc).x + 'px';
            s.width = pc.clientWidth + 'px';
        }
        resizeUArrow()
        window.addEventListener('resize', resizeUArrow);
    })();
</script>

    
</main>
    </div>
    <style>
    img#go-top {
        position: fixed;
        bottom: 100px;
        width: 50px;
        cursor: pointer;
        z-index: 9999;
    }
</style>
<img id="go-top" src="/blog/icon/top.png" class="hidden-xs" style="display: none" />
<script>
    (function () {
        var goTop = document.getElementById('go-top');
        var mainContainer = document.querySelector('.main-container');
        
        goTop.addEventListener('click', function () {
            window.scroll(0, 0);
        }, false);
        window.addEventListener('scroll', function () {
            var right = document.body.offsetWidth - mainContainer.getBoundingClientRect().right;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            goTop.style.right = right + 10 + 'px'
            scrollTop > 700 && (goTop.style.display = "block");
            scrollTop <= 700 && (goTop.style.display = "none");
        });
    })();
</script>
    <style>
    #geektutu-click-img-container {
        position: fixed;
        left: 0;
        top: 0;
        text-align: center;
        width: 100%;
        display: none;
        z-index: 9999;
    }

    #geektutu-click-img-container img {
        object-fit: contain;
        background: #eaecef;
        padding: 15px;
        border-radius: 10px;
        height: auto;
        width: auto;
        vertical-align: middle;
    }
</style>


<div id="geektutu-click-img-container">
    <img src="" alt="Big Image">
</div>

<script>
    (function () {
        var container = document.querySelector('#geektutu-click-img-container')
        var targetImg = container.querySelector('img')
        var imgs = document.querySelectorAll('article img');
        targetImg.addEventListener('click', function (e) {
            container.style.display = 'none';
            e && e.stopPropagation && e.stopPropagation();
        }, false);

        for (var i = 0; i < imgs.length; ++i) {
            var img = imgs[i];
            img.addEventListener('click', (function (src, rate) {
                return function (e) {
                    e && e.stopPropagation && e.stopPropagation();
                    if (window.innerWidth < 980) {
                        return
                    }
                    targetImg.style.height = targetImg.style.width = 'auto';
                    if (window.innerWidth / window.innerHeight > rate) {
                        targetImg.style.height = (window.innerHeight - 20) + 'px';
                    } else {
                        targetImg.style.width = (window.innerWidth - 20) + 'px';
                    }
                    container.style.height = window.innerHeight + 'px'
                    container.style.lineHeight = window.innerHeight + 'px'
                    container.style.display = 'block';
                    targetImg.src = src;
                };
            }(img.src, img.width / img.height)), false)
        }
    })();
</script>
    <!-- Footer -->
    <!-- Footer -->
<style>
    footer {
        width: 100%;
        line-height: 1.5;
        padding: 20px;
    }

    footer a {
        color: #333;
        text-decoration: none;
    }

    .footer-hexo img {
        height: 20px;
        margin-bottom: -5px;
    }

    .footer-hexo a {
        color: #337ab7;
    }
</style>
<footer class="text-center col-xs-12">
    <p>
        <small>© 2020 - Evrn - </small>
        <small>
            <a target="_blank" rel="nofollow noopener" href="http://www.miitbeian.gov.cn/"></a>
        </small>
    </p>
    <p class="footer-hexo">
        <!-- 但若直接使用或修改主题，请务必保留这段声明 -->
        <small>Powered by <a target="_blank" href="https://hexo.io">Hexo</a> | Theme
            <a target="_blank" href="https://geektutu.com">Geektutu</a>
            <a href="https://github.com/geektutu/hexo-theme-geektutu" target="_blank" rel="noopener">
                <img src=""
                    alt="Github Star">
            </a>
        </small>
    </p>
    
    <p>
        <small>
            <span id="busuanzi_container_site_pv">👁<span id="busuanzi_value_site_pv"></span></span> &nbsp;
            <span id="busuanzi_container_page_pv">📚<span id="busuanzi_value_page_pv"></span></span>
        </small>
    </p>
    
    

</footer>


<script>
    window.addEventListener('load', function () {
        globalAddScript('//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js');
    })
</script>

<!-- Baidu Tongji -->





    <script>
        // 非本站网址，新开tab页签
        (function () {
            var stopBubble = function (e) {
                e && e.stopPropagation && e.stopPropagation();
            }
            var links = document.querySelectorAll('a');
            [].slice.call(links).forEach(function (item) {
                if (item.href && item.href.indexOf(window.location.host) === -1) {
                    item.target = '_blank'
                }
                // 阻止冒泡，不触发彩蛋。
                item.addEventListener('click', stopBubble, false);
            });
            var article = document.querySelector('article');
            article && article.addEventListener('click', stopBubble, false)
        })();
    </script>
    
</body>

</html>